fix(tables): server-authoritative run badge, tail SSE from latest, harden Stop-all#5492
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
PR SummaryMedium Risk Overview Run badge & API: Drops SSE: Fresh mounts connect without Header & cells: Stop-all / runs: Dispatch row is inserted before long prep so early Stop-all can cancel; Reviewed by Cursor Bugbot for commit a5a39a8. Bugbot is set up for automated code reviews on this repo. Configure here. |
|
@greptile review |
Greptile SummaryThis PR replaces fragile per-event ±1 client-side run-count deltas with a server-authoritative throttled-refetch model and fixes the reload animation storm caused by replaying buffered SSE history over fresh DB state on mount.
Confidence Score: 5/5Safe to merge. The changes are well-reasoned rewrites of previously fragile client-side delta logic, and all edge cases are guarded. The throttled run-state refetch uses a dirty-flag follow-up so the badge cannot freeze; the prep failure try-catch always re-throws the original error; readDispatch post-prep correctly returns null to prevent firing the dispatcher for a cancelled dispatch; all previous review findings are addressed. No files require special attention — the API contract change is propagated through every consumer. Important Files Changed
Sequence Diagram%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant C as Client
participant SQ as Dispatches API
participant SS as SSE Stream
participant DB as Database
Note over C,DB: Fresh mount
C->>SQ: GET /dispatches
SQ->>DB: countRunningCells + listActiveDispatches
DB-->>SQ: runningByRowId, hasRunning, dispatches
SQ-->>C: run-state snapshot
C->>SS: GET /events/stream (no from)
SS->>DB: getLatestTableEventId
DB-->>SS: latestId
SS-->>C: stream from latestId only
Note over C,DB: Cell events burst
SS-->>C: cell event N
C->>C: applyCell + scheduleDispatchInvalidate (leading)
C->>SQ: GET /dispatches (cancelRefetch false)
SQ-->>C: updated counts
SS-->>C: cell events N+1 to N+20
C->>C: trailing timer fires after 1s
C->>SQ: GET /dispatches
SQ-->>C: final counts
Note over C,DB: Stop-all
C->>C: cancelQueries + optimistic zero
C->>SQ: POST /cancel
SQ->>DB: markActiveDispatchesCancelled
SQ-->>C: 200 OK
C->>SQ: invalidateQueries onSettled
SQ-->>C: reconciled run-state
Note over C,DB: Reconnect after error
SS--xC: connection lost
C->>SS: "GET /events/stream?from=lastEventId"
SS-->>C: replay and resume
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant C as Client
participant SQ as Dispatches API
participant SS as SSE Stream
participant DB as Database
Note over C,DB: Fresh mount
C->>SQ: GET /dispatches
SQ->>DB: countRunningCells + listActiveDispatches
DB-->>SQ: runningByRowId, hasRunning, dispatches
SQ-->>C: run-state snapshot
C->>SS: GET /events/stream (no from)
SS->>DB: getLatestTableEventId
DB-->>SS: latestId
SS-->>C: stream from latestId only
Note over C,DB: Cell events burst
SS-->>C: cell event N
C->>C: applyCell + scheduleDispatchInvalidate (leading)
C->>SQ: GET /dispatches (cancelRefetch false)
SQ-->>C: updated counts
SS-->>C: cell events N+1 to N+20
C->>C: trailing timer fires after 1s
C->>SQ: GET /dispatches
SQ-->>C: final counts
Note over C,DB: Stop-all
C->>C: cancelQueries + optimistic zero
C->>SQ: POST /cancel
SQ->>DB: markActiveDispatchesCancelled
SQ-->>C: 200 OK
C->>SQ: invalidateQueries onSettled
SQ-->>C: reconciled run-state
Note over C,DB: Reconnect after error
SS--xC: connection lost
C->>SS: "GET /events/stream?from=lastEventId"
SS-->>C: replay and resume
Reviews (8): Last reviewed commit: "fix(tables): widen warm-cache remount ch..." | Re-trigger Greptile |
…ck only on fetch start
|
@greptile review |
Greptile SummaryThis PR hardens the table run UI across three axes: badge accuracy, SSE mount behavior, and Stop-all reliability. The "X running" badge moves from fragile client-side ±1 cell deltas to a server-refetched snapshot throttled at 1 s with a dirty-flag follow-up to prevent freeze on slow fetches; optimistic bumps now cancel any in-flight query before writing to avoid stale responses clobbering the count.
Confidence Score: 4/5Safe to merge. The core data paths are server-authoritative now, tests cover the new event-buffer and contract shapes, and the Stop-all race condition fix is solid. The change is well-tested and the main logic — server-refetched badge, tail-from-latest SSE, dispatch-first insertion — is sound. The only noteworthy limitation is hasRunningCell: when the dispatcher's active batch has scrolled entirely out of the loaded viewport, the header flips back from 'N running' to 'Queueing' mid-run. This is called out in the PR description as an intentional tradeoff, but it is a visible inaccuracy in a scenario reachable with large tables. apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table-grid/table-grid.tsx (hasRunningCell limitation) and apps/sim/lib/table/workflow-columns.ts (readDispatch imported separately from other dispatcher functions). Important Files Changed
Sequence Diagram%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant Client
participant SSEHook as useTableEventStream
participant RQ as React Query Cache
participant Server as API / DB
Note over Client,Server: Fresh mount — tail from latest
Client->>Server: GET /dispatches (fetchTableRunState)
Server-->>RQ: "{ dispatches, runningByRowId }"
Client->>Server: GET /events/stream (no `from`)
Server->>Server: getLatestTableEventId()
Note over Server: lastEventId = N (no replay)
Server-->>SSEHook: stream starts at event N+1
Note over Client,Server: User clicks Run All
Client->>RQ: cancelQueries(activeDispatches)
Client->>RQ: setQueryData (optimistic bump)
Client->>Server: POST runWorkflowColumn
Server->>Server: insertDispatch (FIRST)
Server->>Server: cancelPriorRuns + bulkClear
Server->>Server: readDispatch (check not cancelled)
Server-->>Client: "{ dispatchId }"
Server-->>SSEHook: cell SSE events
SSEHook->>SSEHook: scheduleDispatchInvalidate (throttled)
SSEHook->>Server: GET /dispatches (refetch)
Server-->>RQ: updated runningByRowId
Note over Client,Server: User clicks Stop All
Client->>RQ: cancelQueries(activeDispatches)
Client->>RQ: setQueryData (optimistic zero)
Client->>Server: POST cancelTableRuns
Server->>Server: markActiveDispatchesCancelled
Server-->>SSEHook: dispatch cancelled SSE
SSEHook->>SSEHook: invalidateDispatchesNow (urgent)
Server-->>RQ: refetch on onSettled
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant Client
participant SSEHook as useTableEventStream
participant RQ as React Query Cache
participant Server as API / DB
Note over Client,Server: Fresh mount — tail from latest
Client->>Server: GET /dispatches (fetchTableRunState)
Server-->>RQ: "{ dispatches, runningByRowId }"
Client->>Server: GET /events/stream (no `from`)
Server->>Server: getLatestTableEventId()
Note over Server: lastEventId = N (no replay)
Server-->>SSEHook: stream starts at event N+1
Note over Client,Server: User clicks Run All
Client->>RQ: cancelQueries(activeDispatches)
Client->>RQ: setQueryData (optimistic bump)
Client->>Server: POST runWorkflowColumn
Server->>Server: insertDispatch (FIRST)
Server->>Server: cancelPriorRuns + bulkClear
Server->>Server: readDispatch (check not cancelled)
Server-->>Client: "{ dispatchId }"
Server-->>SSEHook: cell SSE events
SSEHook->>SSEHook: scheduleDispatchInvalidate (throttled)
SSEHook->>Server: GET /dispatches (refetch)
Server-->>RQ: updated runningByRowId
Note over Client,Server: User clicks Stop All
Client->>RQ: cancelQueries(activeDispatches)
Client->>RQ: setQueryData (optimistic zero)
Client->>Server: POST cancelTableRuns
Server->>Server: markActiveDispatchesCancelled
Server-->>SSEHook: dispatch cancelled SSE
SSEHook->>SSEHook: invalidateDispatchesNow (urgent)
Server-->>RQ: refetch on onSettled
|
…st on seq-read errors
|
@greptile review |
… run resolves without a dispatch
|
@greptile review |
|
@greptile review |
… snapshot restore
|
@greptile review |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 0c7e829. Configure here.
|
@greptile review |

Summary
runningCellCount; refetch the run-state snapshot on a leading+trailing 1s throttle as cell SSE events arrive (cancelRefetch: false+ dirty-flag follow-up so slow fetches can't livelock or strand a stale final count)countActiveRunCells' per-refetch rows-ahead COUNT deletedType of Change
Testing
Tested manually (run-all/stop-all cycles, reload mid-run, batch transitions). Added tests for the events buffer memory path, dispatches route shape, and the stream
fromparam. Full lint, type-check, check:api-validation:strict, check:react-query pass.Checklist
🤖 Generated with Claude Code
https://claude.ai/code/session_013d4zXsMMwVBmXk33JBok7d